/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.util.core;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.util.ApplicationResourceStorage;
import com.blandware.atleap.model.core.ContentLocale;
import com.blandware.atleap.service.core.ApplicationResourceManager;
import com.blandware.atleap.service.exception.BeanNotFoundException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * <p>Facade class to get messages from application resources storage.<br />
 * Caches all messages and serves as bridge between client and application resources manager.
 * </p>
 * <p><a href="ApplicationResourceFacade.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.10 $ $Date: 2006/08/03 10:08:26 $
 * @see com.blandware.atleap.common.util.ApplicationResourceStorage
 * @see com.blandware.atleap.service.core.ApplicationResourceManager
 */
public final class ApplicationResources {

    protected transient final Log log = LogFactory.getLog(ApplicationResources.class);

    /**
	 * Servlet context key under which instance of this class is saved
	 */
	private static final String INSTANCE_KEY = "com.blandware.atleap.webapp.util.ApplicationResources.INSTANCE";

	/**
	 * Application resource storage
	 */
	private ApplicationResourceStorage applicationResourceStorage;

	/**
	 * Application resource manager we're using
	 */
	private ApplicationResourceManager applicationResourceManager;

	/**
	 * Servlet context of our application
	 */
	private ServletContext servletContext;

	/**
	 * Creates new instance of ApplicationResources and initializes internal fields
	 *
	 * @param servletContext the servlet context
	 */
	private ApplicationResources(ServletContext servletContext) {
		this.servletContext = servletContext;
		ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
		applicationResourceManager = (ApplicationResourceManager) applicationContext.getBean(Constants.APPLICATION_RESOURCE_MANAGER_BEAN);
        loadResources();
    }

	/**
	 * Returns instance of GlobalPropertyManager
	 *
     * @param servletContext the servlet context for which to obtain instance
	 * @return Instance of GlobalPropertyManager
	 */
	public static ApplicationResources getInstance(ServletContext servletContext) {
		ApplicationResources applicationResources = (ApplicationResources) servletContext.getAttribute(INSTANCE_KEY);
		if ( applicationResources == null ) {
			applicationResources = new ApplicationResources(servletContext);
			servletContext.setAttribute(INSTANCE_KEY, applicationResources);
		}
		return applicationResources;
	}

	/**
	 * Updates resource in cache and calls to delegate in order to update resource in persistence layer
	 *
	 * @param localeIdentifier Identifier of locale
	 * @param key              Key to associate value with
	 * @param value            Value to associate with key
     * @param version          Version of application resource
	 * @throws com.blandware.atleap.service.exception.BeanNotFoundException
	 *          if resource couldn't be found
	 */
	public synchronized void updateResource(String localeIdentifier, String key, String value, Long version) throws BeanNotFoundException {
		applicationResourceManager.updateApplicationResource(localeIdentifier, key, value, version);
		applicationResourceStorage.putMessage(new Locale(localeIdentifier), key, value);
	}

	/**
	 * Loads bundle for specified locale and merges with existent
	 *
	 * @param locale Content locale to load bundle for
	 */
	public void loadBundle(ContentLocale locale) {
		ApplicationResourceStorage.Bundle bundle = applicationResourceManager.loadBundle(locale);
		applicationResourceStorage.mergeBundle(bundle);
	}

	/**
	 * Drops bundle for locale with specified identifier
	 *
	 * @param localeIdentifier Identifier of locale to flush bundle
	 */
	public void dropBundle(String localeIdentifier) {
		applicationResourceStorage.dropBundle(new Locale(localeIdentifier));
		applicationResourceManager.dropBundle(localeIdentifier);
	}

	// ~ Methods for retrieving message from bundle

	/**
	 * Returns message for specified key and locale that is stored in session
	 *
     * @param request The request by which user locale is found
	 * @param key Key to get message for
	 * @return Message associated with specified key and locale that is stored in session
	 */
	public String getMessage(HttpServletRequest request, String key) {
		return getMessage(request, key, null);
	}

	/**
	 * Returns message for specified key and locale that is stored in session
	 *
     * @param request The request by which user locale is found
	 * @param key  Key to get message for
	 * @param args Arguments to insert into placeholders
	 * @return Message for specified key and locale stored that is stored in session
	 */
	public String getMessage(HttpServletRequest request, String key, Object[] args) {
		Locale locale = WebappUtil.getEnsuredUserLocale(request);
		return getMessage(locale, key, args);
	}

	/**
	 * Returns message for specified key and given locale
	 *
     * @param locale The locale to get message for
	 * @param key Key to get message for
	 * @return Message associated with specified key and given locale
	 */
	public String getMessage(Locale locale, String key) {
		return getMessage(locale, key, null);
	}

	/**
	 * Returns message for specified key and given locale
	 *
     * @param locale The locale to get message for
	 * @param key  Key to get message for
	 * @param args Arguments to insert into placeholders
	 * @return Message associated with specified key and given locale
	 */
	public String getMessage(Locale locale, String key, Object[] args) {
		String msg = applicationResourceStorage.getMessage(locale, key, args);
		if ( msg == null ) {
			String language = locale.getLanguage();
			// get message for default locale
			LocaleUtil localeUtil = LocaleUtil.getInstance(servletContext);
			ContentLocale defaultLocale = localeUtil.getDefaultLocale();
			if ( !defaultLocale.getIdentifier().equalsIgnoreCase(language) ) {
				msg = applicationResourceStorage.getMessage(new Locale(defaultLocale.getIdentifier()), key, args);
			}

			// if still null, search for the rest of locales
			if ( msg == null ) {
				List activeLocales = localeUtil.getActiveLocales();
				for ( Iterator i = activeLocales.iterator(); i.hasNext() && msg == null; ) {
					ContentLocale contentLocale = (ContentLocale) i.next();

					// skip search for specified locale and for default locale because search for them has already been performed
					String identifier = contentLocale.getIdentifier();
					if ( !identifier.equalsIgnoreCase(language) && !contentLocale.getDefaultInstance().booleanValue() ) {
						msg = applicationResourceStorage.getMessage(new Locale(contentLocale.getIdentifier()), key, args);
					}
				}
			}
		}
		return msg;
	}

	/**
	 * Returns set of keys, stored in bundle, associated with specified locale
	 *
	 * @param contentLocale Locale to get bundle keys
	 * @return Set of keys, stored in bundle, associated with specified locale
	 */
	public Set getKeys(ContentLocale contentLocale) {
		String localeIdentifier = contentLocale.getIdentifier();
		Locale locale = new Locale(localeIdentifier);
		return applicationResourceStorage.getKeys(locale);
	}

	/**
	 * Returns set of keys, stored in bundle, associated with default locale
	 *
	 * @return Set of keys, stored in bundle, associated with default locale
	 */
	public Set getKeys() {
		ContentLocale defaultLocale = LocaleUtil.getInstance(servletContext).getDefaultLocale();
		return getKeys(defaultLocale);
	}

	/**
	 * Reloads all resources from file system and override dynamic values
	 */
	public void reloadResources() {
		applicationResourceStorage = applicationResourceManager.loadResources(true);
	}

    /**
     * Reloads all resources from file system and do not override dynamic values
     */
    public void loadResources() {
        applicationResourceStorage = applicationResourceManager.loadResources(false);
        if ( log.isInfoEnabled() ) {
            log.info("Application resources synchronized");
        }
    }

}
